from pyparsing import *
import string
import Scope

def operatorOperands(tokenlist):
    "generator to extract operators and operands in pairs"
    it = iter(tokenlist)
    while 1:
        try:
            yield (it.next(), it.next())
        except StopIteration:
            break


  
def intParseAction(s, loc, toks):
    return int(toks[0])

def floatParseAction(s, loc, toks):
    return float(toks[0])

def varParseAction(s, loc, toks):
    varname = toks[0]
    if Scope.doesVarExists(varname):
        return Scope.get_var(varname)
    else:
        raise TypeError("Unknown Variable: "+varname)

  
def signParseAction(s, loc, toks):
    sign = toks[0][0]
    val = toks[0][1]
    mult = {'+':1, '-':-1}[sign]
    return mult*val

def sumParseAction(s, loc, toks):
    value = toks[0]
    prod = value[0]
    for op,val in operatorOperands(value[1:]):
        if op == '+':
            prod+=val
        else:
            prod-=val
    return prod    

def mulParseAction(s, loc, toks):
    value = toks[0]
    prod = value[0]
    for op,val in operatorOperands(value[1:]):
        if op == '*':
            prod*=val
        else:
            prod/=val
    return prod    

### BNF Dictionary:
          
#Literals:
Comma          = Literal(',')
Underscore     = Literal('_')
LEFT_PAR       = Literal('(')
RIGHT_PAR      = Literal(')')
LEFT_BRAC      = Literal('{')
RIGHT_BRAC     = Literal('}')
EQUALS         = Literal('=')
PLUS           = Literal('+')
MINUS          = Literal('-')
TIMES          = Literal('*').suppress()
signOp         = oneOf('+ -')
multOp         = oneOf('* /')
sumOp          = oneOf('+ -')

# Nums:
Digits         = string.digits
Integer        = Word(Digits).setParseAction(intParseAction)
Float          = Combine(Integer + '.' + Optional(Integer, default='0')).setParseAction(floatParseAction)


            
# Literals and building blocks:
UCase          = string.uppercase
LCase          = string.lowercase
Letters        = UCase+LCase
VarChars       = Letters + Digits + '_'

# Atoms:
Numeric        = Float | Integer
Variable       = Word(Letters, VarChars)('var')
ExistingVar    = Variable.setParseAction(varParseAction)
Assignee       = Word(Letters, VarChars)

#Arithmatic:
Term           = Numeric | ExistingVar


ArithExpression= operatorPrecedence( Term,
                                          [(signOp, 1, opAssoc.RIGHT, signParseAction),
                                           (multOp, 2, opAssoc.LEFT, mulParseAction),
                                           (sumOp, 2, opAssoc.LEFT, sumParseAction)
                                           ])
VarAssignment  = Assignee + Literal("=").suppress()+ ArithExpression


varassignmentTest = """
a = -1 +(2.0/ 5.1)*b+c
"""

if __name__=='__main__':
    header = 'Parser Tester begins:\n'
    print header + '=' * (len(header)-1)
    print varassignmentTest
    print '-' * (len(header)-1) + '\n'
    Scope.add_var("b",5)
    print "b = 5"
    Scope.add_var("c",-1)
    print "c = -1"
    try:
        results = VarAssignment.parseString(varassignmentTest)
        print '-' * (len(header)-1)
        print results
    except ParseException, pe:
        print pe
